﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;

public class HUD
{
    // Consts here
    private static Vector2 kFrameOffset = new Vector2(Game.kScreenWidth - 258, 7);
    private static Vector2 kMapOffset = new Vector2(Game.kScreenWidth - 256, 9);
    private static Vector2 kMapStringOffset = new Vector2(Game.kScreenWidth - 249, Game.kScreenHeight / 4);
    private static Rectangle kMapBounds = new Rectangle((int)kMapOffset.X, (int)kMapOffset.Y, 247, 132);
    private static int kSteveGraphicHeight = 93;
    private static int kObjectiveGraphicHeight = 12;
    private static int kWorldWidth = 10500;
    private static int kGroundLevel = 5000;
    private static int kSteveFootWidth = 1107;
    private static int kSteveGraphicWidth = 109;

    // Member vars here
    private String m_noticeString = "";
    private String m_minimapString = "";
    private int m_findTotal;
    private int m_findCounter;
    private double m_noticeTimer;

    private Steve m_steveRef;

    private Vector2 m_stevePos;
    private List<Vector2> m_objectPos;
    private Vector2 m_playerPos;

    private static SpriteFont s_noticeFont = null;
    private static SpriteFont s_minimapFont = null;

    private Animation m_mapFrame;
    private Animation m_mapGraphic;
    private Animation m_steve;
    private Animation m_marker;
    private Animation m_player;

    private static int kNoticeXPadding = 120;
    private static int kNoticeYPadding = 20;
    private static int kBackgroundSize = 8;

    /**
     * Statically load the fonts once.
     */
    public static void loadFonts(ContentManager content)
    {
        s_noticeFont = content.Load<SpriteFont>("fonts/NoticeFont");
        s_minimapFont = content.Load<SpriteFont>("fonts/MinimapFont");
    }

    /**
     * Constructor.
     */
    public HUD()
    {
        m_steveRef = null;
        m_stevePos = Vector2.Zero;
        m_objectPos = new List<Vector2>();

        m_noticeTimer = 5000;
        loadAssets();
    }

    /**
     * Load up our graphics.
     */
    private void loadAssets()
    {
        m_mapFrame = new Animation("miniMapFrame", 0, false);
        m_mapFrame.addFrame(
            new Frame(
                Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.GUI).get("miniMapFrame"),
                new Rectangle(0, 0, 251, 168),
                0, Vector2.Zero
                ));

        m_mapGraphic = new Animation("miniMapCityBG", 0, false);
        m_mapGraphic.addFrame(
            new Frame(
                Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.GUI).get("miniMapCityBG"),
                new Rectangle(0, 0, 247, 132),
                0, Vector2.Zero
                ));

        m_steve = new Animation("miniMapSteveWarning", 0, false);
        m_steve.addFrame(
            new Frame(
                Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.GUI).get("miniMapSteveWarning"),
                new Rectangle(0, 0, 109, 97),
                // Offsetting the center by a small bit to make the foot coincide more
                0, new Vector2(109/2, 0)
                ));

        m_marker = new Animation("miniMapMissionObjectiveDot", 0, false);
        m_marker.addFrame(
            new Frame(
                Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.GUI).get("miniMapMissionObjectiveDot"),
                new Rectangle(0, 0, 11, 12),
                0, new Vector2(11/2, 0)
                ));

        m_player = new Animation("miniMapMissionObjectiveDot", 0, false);
        m_player.addFrame(
            new Frame(
                Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.GUI).get("miniMapMissionObjectiveDot"),
                new Rectangle(0, 0, 11, 12),
                0, new Vector2(11/2, 0)
                ));
    }

    /**
     * Takes a world coord for Steve's current location, and translates
     * that to a marker on screen. Works under the assumption that Steve
     * will never actually be at world position 0,0, as he won't be
     * drawn on the minimap during that time.
     */
    public void setStevePosition(Vector3 position, Steve steve)
    {
        if (m_steveRef == null)
        {
            m_steveRef = steve;
        }
        // 1107 = Width of Steve's food graphic
        m_stevePos = worldToMinimap(position);
        m_stevePos.Y -= kSteveGraphicHeight;
    }

    /**
     * Takes a list of positions for all mission objects we are currently
     * tracking, and puts markers on the minimap.
     */
    public void setMissionObjectPositions(List<Vector3> positionList)
    {
        m_objectPos.Clear();
        foreach (Vector3 vec in positionList)
        {
            Vector2 position = worldToMinimap(vec);
            position.Y -= kObjectiveGraphicHeight;
            m_objectPos.Add(position);
        }
    }

    /**
     * Takes a world coord for the player's current position. Draws a marker
     * on the map representing that position.
     */
    public void setPlayerPosition(Vector3 position)
    {
        m_playerPos = worldToMinimap(position);
        m_playerPos.Y -= kObjectiveGraphicHeight;
    }

    /**
     * Used internally to convert world positions to the appropriate
     * spot on the minimap.
     */
    private Vector2 worldToMinimap(Vector3 worldPos)
    {
        Vector2 mapPos = Vector2.Zero;
        mapPos.X = kMapBounds.X + (worldPos.X / (kWorldWidth + kSteveFootWidth)) * (kMapBounds.Width - kSteveGraphicWidth) + kSteveGraphicWidth / 2;
        mapPos.Y = kMapBounds.Y + (worldPos.Y / kGroundLevel) * kMapBounds.Height;

        return mapPos;
    }

    /**
     * Set the message for our notification window.
     * @param message - Message to display.
     * @param millisToShow - Time before the message disappears
     */
    public void setNoticeMessage(String message, double millisToShow)
    {
        m_noticeString = message;
        m_noticeTimer = millisToShow;
    }

    /**
     * Set the message for our minimap.
     * @param message - Message to display
     */
    public void setMinimapMessage(String message, int toFind)
    {
        m_minimapString = message;
        m_findTotal = toFind;
        m_findCounter = 0;
    }
    /**
     * Increment the count on our minimap.
     */
    public void incrementFound()
    {
        m_findCounter++;
        if (m_findCounter > m_findTotal)
        {
            m_findCounter = m_findTotal;
        }
    }

    /**
     * Updates any logics we'd want to update.
     */
    public void update(double dt)
    {
        // Decrement timer and stop displaying our string if necessary
        if(m_noticeString.CompareTo("") != 0)
        {
            m_noticeTimer -= dt;
            if(m_noticeTimer <= 0.0)
            {
                m_noticeString = "";
            }
        }
    }

    /**
     * Renders our minimap, dots, steve, and any messages
     */
    public void render()
    {
        m_mapFrame.render(kFrameOffset, Priority.kHUDMid);
        m_mapGraphic.render(kMapOffset, Priority.kHUDFar);

        // Draw our steve at given position
        if (m_stevePos != Vector2.Zero)
        {
            if (m_steveRef.getDirection() == Steve.Direction.Left)
            {
                m_steve.render(m_stevePos, Color.White, SpriteEffects.None, Priority.kHUDMid);
            }
            else
            {
                m_steve.render(m_stevePos, Color.White, SpriteEffects.FlipHorizontally, Priority.kHUDMid);
            }
        }
        // Draw all mission object markers
        foreach(Vector2 vec in m_objectPos)
        {
            m_marker.render(vec, Color.White, SpriteEffects.None, Priority.kHUDNear);
        }
        m_player.render(m_playerPos, Color.Red, SpriteEffects.None, Priority.kHUDNear);

        // Construct our minimap text
        String minimapString = m_minimapString + " " + m_findCounter + "/" + m_findTotal;

        // Get dimensions of text to draw
        Vector2 noticeDim = s_noticeFont.MeasureString(m_noticeString);
        Texture2D texture = Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.OUTLINE).get("outline").spriteSheet;
        
        // If there's text for the minimap, draw it
        if (m_minimapString.CompareTo("") != 0)
        {
            Game.getGame().getSpriteBatch().DrawString(s_minimapFont, minimapString,
                kMapStringOffset, Color.White, 0.0f,
                Vector2.Zero, 1.0f, SpriteEffects.None, Priority.kHUDNear);
        }

        // If there's a notice message, draw a box behind it, then draw the message itself
        if(m_noticeString.CompareTo("") != 0)
        {
            // Do some random fading-away when there's 1-second left, for no reason.
            Color noticeBG = Color.White;
            Color noticeColor = Color.Black;
            if (m_noticeTimer <= 1000.0)
            {
                noticeBG.A = (byte)(255 * (m_noticeTimer / 1000.0));
                noticeColor.A = (byte)(255 * (m_noticeTimer / 1000.0));
            }

            Game.getGame().getSpriteBatch().Draw(texture, new Vector2(Game.kScreenWidth / 2, Game.kScreenHeight / 4), null,
                noticeBG, 0.0f, new Vector2(kBackgroundSize / 2, kBackgroundSize / 2),
                new Vector2(noticeDim.X / kBackgroundSize + kNoticeXPadding / kBackgroundSize, noticeDim.Y / kBackgroundSize + kNoticeYPadding / kBackgroundSize),
                SpriteEffects.None, Priority.kHUDFar);
            Game.getGame().getSpriteBatch().DrawString(s_noticeFont, m_noticeString,
                new Vector2(Game.kScreenWidth / 2, Game.kScreenHeight / 4), noticeColor, 0.0f,
                new Vector2(noticeDim.X / 2, noticeDim.Y / 2), 1.0f, SpriteEffects.None, Priority.kHUDNear);
        }
    }
}
